package trace

import (
	"bytes"
	"fmt"
	"runtime"
	"strconv"
	"sync"
)

var goroutineSpace = []byte("goroutine ")

func curGoroutineID() uint64 {
	b := make([]byte, 64)
	b = b[:runtime.Stack(b, false)]
	// Parse the 4707 out of "goroutine 4707 ["
	b = bytes.TrimPrefix(b, goroutineSpace)
	i := bytes.IndexByte(b, ' ')
	if i < 0 {
		panic(fmt.Sprintf("No space found in %q", b))
	}
	b = b[:i]
	n, err := strconv.ParseUint(string(b), 10, 64)
	if err != nil {
		panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err))
	}
	return n
}

func printTrace(gid uint64, name, arrow string, indent int) {
	// indents := strings.Builder{}
	indents := ""

	for i := 0; i < indent; i++ {
		// _, err := indents.WriteString("  ")
		indents += "    "
		// if err != nil {
		// 	panic("write indents failed")
		// }
	}

	fmt.Printf("g[%05d]:%s%s%s\n", gid, indents, arrow, name)
}

var mu sync.Mutex
var m = make(map[uint64]int) // 保存每个 Goroutine 当前的缩进信息，key 是Goroutine Id；value:缩进层次

func Trace() func() {
	pc, _, _, ok := runtime.Caller(1)
	if !ok {
		panic("not found caller")
	}

	fn := runtime.FuncForPC(pc)
	name := fn.Name()

	gid := curGoroutineID()

	mu.Lock()
	// map 不支持并发，这里使用并发锁同步对 m 的写操作
	m[gid] += 1 // 每次调用，缩进+1
	indents := m[gid]
	mu.Unlock()

	printTrace(gid, name, "->", indents)

	return func() {
		// 调用函数的起止位置的缩进相同
		printTrace(gid, name, "<-", m[gid])

		mu.Lock()

		// 打印输出之后，将要返回上一层调用，此时缩进-1
		m[gid] -= 1 // 每次结束调用，缩进-1
		mu.Unlock()
	}
}
